/**
 * 全局请求封装（基于 uni.request）
 * - 自动拼接 BASE_URL
 * - 可选 Token 注入（auth: true 时）
 * - 统一错误处理与 Toast 提示
 * - 提供 get/post/put/delete 便捷方法
 */
import { withBase } from '@/config/env';
declare const uni: any;

type AnyObj = Record<string, any>;

export interface RequestOptions<T = any> {
  /** 完整或相对路径（相对路径将拼接 BASE_URL） */
  url: string;
  /** 请求方法 */
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'HEAD' | string;
  /** 请求体（POST/PUT）或后端接受的对象 */
  data?: Record<string, any>;
  /** 查询参数（GET 等将被拼接到 URL） */
  params?: Record<string, any>;
  /** 头信息 */
  header?: Record<string, any>;
  /** 其他可选项（按需） */
  timeout?: number;
  dataType?: string;
  responseType?: 'text' | 'arraybuffer' | string;
  sslVerify?: boolean;
  withCredentials?: boolean;
  firstIpv4?: boolean;
  /** 是否携带 token（默认 true，显式设为 false 可关闭） */
  auth?: boolean;
  /** 响应数据的泛型（占位，不参与运行时） */
  _type?: T;
}

export interface RequestError extends Error {
  code?: number | string;
  statusCode?: number;
  data?: any;
}

function showToast(title: string) {
  if (!title) return;
  uni.showToast({ title, icon: 'none', duration: 2000 });
}

function getToken(): string | undefined {
  try {
    return uni.getStorageSync('token') || undefined;
  } catch {
    return undefined;
  }
}

function buildHeaders(opts?: { header?: AnyObj }): AnyObj {
  const headers: AnyObj = {
    'Content-Type': 'application/json',
    ...(opts?.header || {}),
  };
  return headers;
}

function appendQuery(url: string, params?: Record<string, any>): string {
  if (!params || typeof params !== 'object') return url;
  const qs = Object.entries(params)
    .filter(([_, v]) => v !== undefined && v !== null)
    .map(([k, v]) => {
      const val = typeof v === 'object' ? JSON.stringify(v) : String(v);
      return `${encodeURIComponent(k)}=${encodeURIComponent(val)}`;
    })
    .join('&');
  if (!qs) return url;
  return url.includes('?') ? `${url}&${qs}` : `${url}?${qs}`;
}

export function request<T = any>(options: RequestOptions<T>): Promise<T> {
  return new Promise((resolve, reject) => {
    const {
      url = '',
      auth = true,
      method = 'GET',
      data,
      header,
      timeout,
      dataType,
      responseType,
      sslVerify,
      withCredentials,
      firstIpv4,
    } = options;

    const baseUrl = withBase(url);
    const isGet = (method || 'GET').toUpperCase() === 'GET';
    const finalUrl = appendQuery(baseUrl, (options as any).params || (isGet ? data : undefined));
    const headers = buildHeaders({ header });

    {
      const token = getToken();
      // 默认携带 token，除非显式传入 auth === false
      if (auth !== false && token) {
        headers['token'] = headers['token'] || `${token}`;
      }
    }

    uni.request({
      url: finalUrl,
      method,
      data: isGet ? undefined : data,
      header: headers,
      timeout,
      dataType,
      responseType,
      sslVerify,
      withCredentials,
      firstIpv4,
      success: (res: any) => {
        const { statusCode, data } = res;
        // 2xx 视为成功
        if (statusCode >= 200 && statusCode < 300) {
          resolve(data as T);
          return;
        }

        // 非 2xx 作为错误处理
        const msg =
          (data && (data.message || data.msg || data.error)) ||
          `Request failed: ${statusCode}`;
        showToast(msg);

        const err: RequestError = new Error(msg);
        err.statusCode = statusCode;
        err.data = data;
        reject(err);
      },
      fail: (err: any) => {
        const msg = err?.errMsg || 'Network Error';
        showToast(msg);
        const error: RequestError = new Error(msg);
        error.code = 'NETWORK_ERROR';
        reject(error);
      },
    });
  });
}

export function get<T = any>(url: string, params?: AnyObj, opts?: Omit<RequestOptions<T>, 'url' | 'method' | 'data'>) {
  return request<T>({ url, method: 'GET', params, ...(opts || {}) });
}

export function post<T = any>(url: string, body?: AnyObj, opts?: Omit<RequestOptions<T>, 'url' | 'method' | 'data'>) {
  return request<T>({ url, method: 'POST', data: body, ...(opts || {}) });
}

export function put<T = any>(url: string, body?: AnyObj, opts?: Omit<RequestOptions<T>, 'url' | 'method' | 'data'>) {
  return request<T>({ url, method: 'PUT', data: body, ...(opts || {}) });
}

export function del<T = any>(url: string, params?: AnyObj, opts?: Omit<RequestOptions<T>, 'url' | 'method' | 'data'>) {
  return request<T>({ url, method: 'DELETE', data: params, ...(opts || {}) });
}